package de.htw.noise;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.VolatileImage;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class PerlinNoiseDemo2 extends JFrame
{
	private int noiseWidth = 480;
	private int noiseHeight = 512;
	
	private int windowWidth = 640;
	private int windowHeight = 640;
	
	private double persistence = .5;
	private int octave = 8;
	
	private int scaleX = 4 * noiseWidth;
	private int scaleY = 4 * noiseHeight;
	
	
	private DecimalFormat format = new DecimalFormat("#.##");
	
	
	private boolean smooth = false;
	
	private PerlinNoise perlinNoise;
	
	private JPanel graphicsPanel;
	private JPanel toolsPanel;
	private JPanel sliderPanel;
	
	private JSlider persistenceSlider;
	private JSlider octaveSlider;
	
	private VolatileImage buffer;
	private Graphics2D bufferGraphics;
	
	private JLabel seedLabel;
	private JLabel colorLabel;
	private JLabel persistenceLabel;
	private JLabel octaveLabel;
	
	private JComboBox gradientBox;
	
	private JTextField seedInput;
	
	private JButton renderButton;
	
	private JCheckBox smoothCheckBox;
	protected long oldSeed;
	
	
	
	private enum ColorMethod { red_blue, black_red_white, red_yellow_green};
	

	public static void main(String[] args)
	{
		PerlinNoiseDemo2 demo = new PerlinNoiseDemo2();
	}
	
	/**
	 * Renders current Perlin Noise function to the graphics buffer
	 * and updates the graphics panel when done. 
	 * 
	 */
	private void render()
	{
		if ((bufferGraphics!=null) && (buffer!=null) && (graphicsPanel!=null))
		{
			Color color;
			
			//for each pixel
			for (int v = 0; v < noiseHeight-1; v++)
			{
				for (int u = 0; u < noiseWidth-1; u++)
				{
					double colorValue;
					
//					if (smooth)
					
//					System.out.println(u + " | " + v);
					
					colorValue = Math.cos(perlinNoise.perlinNoise2D(u / (double)noiseWidth,v / (double)noiseHeight) * u);
						//TODO ???
//						colorValue = perlinNoise.perlinNoise2D( ((1 +u) / (scaleX - u)),  ((1 +v) / (scaleY - v)));
//					else
//						colorValue = perlinNoise.noise2D(u, v);
					
					
//					if (u>0)
//						colorValue = -1d+((double)u/(double)noiseWidth)*2d;
//					else
//						colorValue = -1;
					
					//get color of the current pixel
					color = getColor(ColorMethod.values()[gradientBox.getSelectedIndex()], colorValue);
					
					//set pixel
					bufferGraphics.setColor(color);
					bufferGraphics.fillRect(u, v, 1, 1);
					
				}
			}
			graphicsPanel.repaint();
		}
	}
	
	public PerlinNoiseDemo2()
	{
		perlinNoise = new PerlinNoise((short) 512, 0.5, 8);
		//create panel for displaying graphics
		graphicsPanel = new JPanel()
		{
			@Override
			public void paint(Graphics g)
			{
				Graphics2D g2d = (Graphics2D)g;
				
				//first time: create the graphics buffer
				if (buffer==null)
				{
					buffer = g2d.getDeviceConfiguration().createCompatibleVolatileImage(noiseWidth, noiseHeight, Transparency.OPAQUE);
					bufferGraphics = (Graphics2D)buffer.getGraphics();
				}
				
				super.paint(g);
				
				//draw buffer
				g2d.drawImage(buffer, 0, 0, null);
				
			}
		};
		
		//intialize labels
		seedLabel = new JLabel("seed:");
		colorLabel = new JLabel("color:");
		
		//initialize input fields
		seedInput = new JTextField("1024");
		
		//initialize gradient combobox
		gradientBox = new JComboBox<ColorMethod>(ColorMethod.values());
		gradientBox.setSelectedIndex(0);
		
		//initialize buttons
		renderButton = new JButton("render");
		renderButton.addActionListener(new ActionListener()
		{
			
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				long seed = Integer.valueOf(seedInput.getText());
				
//				if ((perlinNoise==null))
					perlinNoise.update((short) seed,persistence,octave);
				
				render();
				
//				oldSeed = seed;
			}


		});
		
		//initialize checkbox for smoothing
		smoothCheckBox = new JCheckBox("smooth");
		smoothCheckBox.addActionListener(new ActionListener()
		{
			
			@Override
			public void actionPerformed(ActionEvent arg0)
			{
				smooth = !smooth;
			}
		});
		
		format.setMinimumFractionDigits(2);
		
		persistenceLabel = new JLabel(" 0.50 ");
		octaveLabel = new JLabel(" 8 ");
		
		persistenceSlider = new JSlider(1, 400, 50);
		persistenceSlider.setOrientation(JSlider.VERTICAL);
		octaveSlider = new JSlider(0, 16, 8);
		octaveSlider.setOrientation(JSlider.VERTICAL);
		
		persistenceSlider.addChangeListener(new ChangeListener()
		{
			@Override
			public void stateChanged(ChangeEvent arg0)
			{
				persistence = (double)(persistenceSlider.getValue()/100d);
				persistenceLabel.setText(" " + format.format(persistence) + " ");
			}
		});
		
		octaveSlider.addChangeListener(new ChangeListener()
		{
			@Override
			public void stateChanged(ChangeEvent arg0)
			{
				octave = octaveSlider.getValue();
				octaveLabel.setText(" " + octave + " ");
			}
		});
		
		//initialize slider panel, add components
		sliderPanel = new JPanel(new GridLayout(2, 2));
		sliderPanel.add(persistenceSlider);
		sliderPanel.add(octaveSlider);
		sliderPanel.add(persistenceLabel);
		sliderPanel.add(octaveLabel);
	
		//initialize tools panel, add components
		toolsPanel = new JPanel(new GridLayout(1, 6));
		toolsPanel.add(seedLabel);
		toolsPanel.add(seedInput);
		toolsPanel.add(renderButton);
		toolsPanel.add(smoothCheckBox);
		toolsPanel.add(colorLabel);
		toolsPanel.add(gradientBox);
		
		//basic window settings
		this.setLayout(new BorderLayout());
		this.add(graphicsPanel, BorderLayout.CENTER);
		this.add(toolsPanel, BorderLayout.SOUTH);
		this.add(sliderPanel, BorderLayout.EAST);
		
		this.setSize(windowWidth, windowHeight);
		this.setLocationRelativeTo(null);
		this.setTitle("Perlin Noise Demo");
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	
	}
	
	/**
	 * Returns the false color for a double value [-1, 1]
	 * 
	 * @param colorMethod The color method {@linkplain ColorMethod}
	 * @param colorValue The color value (double between -1 and 1)
	 * @return java.awt.Color
	 */
	public Color getColor(ColorMethod colorMethod, double colorValue)
	{
		int r = 0, b = 0, g = 0;
		
		double signedColorValue = (1d+colorValue)/2d; 
		double mid = 1d-Math.abs(colorValue);
		
		if (colorMethod.equals(ColorMethod.red_blue))
		{
			r = 255-(int)(signedColorValue*255);
			b = (int)(signedColorValue*255);
		}
		else if (colorMethod.equals(ColorMethod.red_yellow_green))
		{
			r = (255-(int)(signedColorValue*255)) + (int)(mid*127);
			g = (int)(signedColorValue*255)  + (int)(mid*127);
		}
		else if (colorMethod.equals(ColorMethod.black_red_white))
		{
			r = (int)(signedColorValue*127) + (int)(mid*127);
			g = (int)(signedColorValue*255);
			b = (int)(signedColorValue*255);
		}
		
		r = Math.max(0,Math.min(r,255));
		g = Math.max(0,Math.min(g,255));
		b = Math.max(0,Math.min(b,255));
		return new Color(r,g,b);
	}

}
